/*
    Matali Physics Demo
    Copyright (c) 2011 KOMIRES Sp. z o. o.
 */
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using Komires.MataliPhysics;

namespace MataliPhysicsDemo
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Camera3Animation1
    {
        Demo demo;
        PhysicsScene scene;
        string instanceIndexName;

        string sphereName;
        string cursorName;
        string shotName;

        Shape sphere;

#if XBOX360
        GamePadState oldPadState;
#else
        MouseState oldMouseState;
#endif

        Vector3 startPosition;
        Vector3 step;
        int stepCount;
        int maxStepCount;

        Vector3 position;
        Matrix rotation;
        Matrix cameraRotation;
        Matrix projection;
        Matrix view;

        Viewport viewport;
        Vector2 mousePosition;

        Vector3 vectorZero;
        Matrix matrixIdentity;
        Quaternion quaternionIdentity;

        public Camera3Animation1(Demo demo, int instanceIndex)
        {
            this.demo = demo;
            instanceIndexName = " " + instanceIndex.ToString();

            sphereName = "Sphere";
            cursorName = "Cursor";
            shotName = "Camera 3 Shot" + instanceIndexName + " ";

            vectorZero = Vector3.Zero;
            matrixIdentity = Matrix.Identity;
            quaternionIdentity = Quaternion.Identity;
        }

        public void Initialize(PhysicsScene scene)
        {
            this.scene = scene;

            startPosition = Vector3.Zero;
            step = Vector3.Zero;
            stepCount = 0;
            maxStepCount = 0;

#if XBOX360
            mousePosition.X = 0.5f * demo.GraphicsDevice.Viewport.Width;
            mousePosition.Y = 0.5f * demo.GraphicsDevice.Viewport.Height;
#endif
        }

        public void SetControllers(Vector3 objectStep, int objectMaxStepCount)
        {
            step = objectStep;
            maxStepCount = objectMaxStepCount;

            sphere = scene.Factory.ShapeManager.Find("Sphere");

            stepCount = 0;

#if XBOX360
            oldPadState = GamePad.GetState(PlayerIndex.One);
#else
            oldMouseState = Mouse.GetState();
#endif

            PhysicsObject objectBase = scene.Factory.PhysicsObjectManager.Find("Camera 3" + instanceIndexName);

            if (objectBase != null)
            {
                objectBase.Camera.Active = false;

                objectBase.UserControllers.TransformMethods += new SimulateMethod(MoveCursor);
                objectBase.UserControllers.PostTransformMethods += new SimulateMethod(Move);
            }
        }

        public void RefreshControllers()
        {
            stepCount = 0;

#if XBOX360
            oldPadState = GamePad.GetState(PlayerIndex.One);
#else
            oldMouseState = Mouse.GetState();
#endif

            PhysicsObject objectBase = scene.Factory.PhysicsObjectManager.Find("Camera 3" + instanceIndexName);
            if (objectBase != null)
                objectBase.Camera.Active = false;
        }

        public void MoveCursor(SimulateMethodArgs args)
        {
            PhysicsScene scene = demo.Engine.Factory.PhysicsSceneManager.Get(args.OwnerSceneIndex);
            PhysicsObject objectBase = scene.Factory.PhysicsObjectManager.Get(args.OwnerIndex);

            if (!objectBase.Camera.Enabled) return;
            if (!objectBase.Camera.Active) return;

            float time = (float)args.Time;

#if !XBOX360
            MouseState mouseState = Mouse.GetState();

            mousePosition.X = mouseState.X;
            mousePosition.Y = mouseState.Y;
#endif

            objectBase.Camera.View.GetViewMatrix(ref view);
            objectBase.Camera.Projection.GetProjectionMatrix(ref projection);
            viewport = demo.GraphicsDevice.Viewport;

            ScreenToRayController screenToRayController = objectBase.InternalControllers.ScreenToRayController;
            screenToRayController.SetViewport(ref viewport);
            screenToRayController.SetViewMatrix(ref view);
            screenToRayController.SetProjectionMatrix(ref projection);
            screenToRayController.SetScreenPosition(ref mousePosition);
            screenToRayController.Update();
        }

        void Move(SimulateMethodArgs args)
        {
            Vector3 cameraPosition;

            PhysicsScene scene = demo.Engine.Factory.PhysicsSceneManager.Get(args.OwnerSceneIndex);
            PhysicsObject objectBase = scene.Factory.PhysicsObjectManager.Get(args.OwnerIndex);

            if (!objectBase.Camera.Enabled) return;

            if (!objectBase.Camera.Active)
            {
                objectBase.InitLocalTransform.GetPosition(ref startPosition);
                Vector3.Multiply(ref step, stepCount, out cameraPosition);
                Vector3.Add(ref cameraPosition, ref startPosition, out cameraPosition);

                if (stepCount <= maxStepCount)
                {
                    objectBase.MainWorldTransform.SetPosition(ref cameraPosition);
                    objectBase.RecalculateMainTransform();
                    stepCount++;
                }
                else
                    objectBase.Camera.Active = true;
            }

            float time = (float)args.Time;

            bool enableShot = false;

#if XBOX360
            GamePadState padState = GamePad.GetState(PlayerIndex.One);

            mousePosition.X += 4.0f * (padState.ThumbSticks.Right.X + padState.ThumbSticks.Left.X);
            mousePosition.Y -= 4.0f * (padState.ThumbSticks.Right.Y + padState.ThumbSticks.Left.Y);

            if (mousePosition.X < 0.0f)
                mousePosition.X = demo.GraphicsDevice.Viewport.Width;

            if (mousePosition.X > demo.GraphicsDevice.Viewport.Width)
                mousePosition.X = 0.0f;

            if (mousePosition.Y < 0.0f)
                mousePosition.Y = demo.GraphicsDevice.Viewport.Height;

            if (mousePosition.Y > demo.GraphicsDevice.Viewport.Height)
                mousePosition.Y = 0.0f;
#else
            MouseState mouseState = Mouse.GetState();

            if ((mouseState.LeftButton == ButtonState.Pressed) && (oldMouseState.LeftButton == ButtonState.Released))
                enableShot = true;

            mousePosition.X = mouseState.X;
            mousePosition.Y = mouseState.Y;

            oldMouseState = mouseState;
#endif

            objectBase.Camera.Projection.CreatePerspectiveLH(1.0f, 10000.0f, 70.0f, demo.Window.ClientBounds.Width, demo.Window.ClientBounds.Height);

            objectBase.MainWorldTransform.GetPosition(ref position);
            objectBase.Camera.GetTransposeRotation(ref cameraRotation);

            objectBase.Camera.View.CreateLookAtLH(ref position, ref cameraRotation, 0.0f);
            objectBase.Camera.UpdateFrustum();

            objectBase.Camera.View.GetViewMatrix(ref view);
            objectBase.Camera.Projection.GetProjectionMatrix(ref projection);
            viewport = demo.GraphicsDevice.Viewport;

            Vector3 rayPosition, rayDirection;

            rayPosition = rayDirection = vectorZero;

            objectBase.UnProjectToRay(ref mousePosition, ref viewport, ref view, ref matrixIdentity, ref projection, ref rayPosition, ref rayDirection);

            PhysicsObject cursor = scene.Factory.PhysicsObjectManager.Find(cursorName);

            if (cursor != null)
            {
                Vector3 cursorPosition = vectorZero;
                Matrix cursorLocalRotation = matrixIdentity;
                Matrix cursorWorldRotation = matrixIdentity;

                cursor.InitLocalTransform.GetPosition(ref cursorPosition);
                cursor.InitLocalTransform.GetRotation(ref cursorLocalRotation);
                cursor.MainWorldTransform.GetRotation(ref cursorWorldRotation);

                objectBase.Camera.GetTransposeRotation(ref cameraRotation);
                Matrix.Multiply(ref cursorLocalRotation, ref cameraRotation, out rotation);

                Vector3.Transform(ref cursorPosition, ref cursorWorldRotation, out position);

                cursor.MainWorldTransform.SetRotation(ref rotation);
                Vector3.Add(ref position, ref rayPosition, out position);
                Vector3.Add(ref position, ref rayDirection, out position);
                cursor.MainWorldTransform.SetPosition(ref position);
            }

            if (enableShot)
            {
                PhysicsObject shot = scene.Factory.PhysicsObjectManager.Create(shotName + scene.SimulationFrameCount.ToString());

                shot.Shape = sphere;
                shot.UserTagStr = sphereName;

                Vector3 shotScale = vectorZero;
                shotScale.X = shotScale.Y = shotScale.Z = 0.5f;

                Vector3.Multiply(ref rayDirection, 1000.0f, out rayDirection);

                shot.InitLocalTransform.SetRotation(ref matrixIdentity);
                shot.InitLocalTransform.SetPosition(ref rayPosition);
                shot.InitLocalTransform.SetScale(ref shotScale);

                shot.InitLocalTransform.SetLinearVelocity(ref rayDirection);
                shot.InitLocalTransform.SetAngularVelocity(ref vectorZero);
                shot.Integral.SetDensity(10.0f);
                shot.MaxSimulationFrameCount = 10;
                shot.EnableCursorInteraction = false;
                shot.EnableResponseToCollisions = false;
                shot.EnableDrawing = false;
                shot.EnableCollisions = true;
                shot.DisableCollision(objectBase, true);
                shot.MaxDisableCollisionFrameCount = 10;

                scene.UpdateFromInitLocalTransform(shot);
            }

            objectBase.Camera.UpdatePhysicsObjects(true, true);
            objectBase.Camera.SortDrawPhysicsObjects(PhysicsCameraSortOrderType.DrawPriorityShapePrimitiveType);
            objectBase.Camera.SortTransparentPhysicsObjects(PhysicsCameraSortOrderType.DrawPriorityShapePrimitiveType);
        }
    }
}
